/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.common.base.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Strings;
import com.je.common.base.exception.PlatformException;
import com.je.common.base.exception.PlatformExceptionEnum;
import com.je.common.base.func.funcPerm.fastAuth.FastAuthVo;
import com.je.common.base.func.funcPerm.fastAuth.FuncQuickPermission;
import com.je.common.base.mapper.query.*;
import com.je.common.base.service.rpc.SystemVariableRpcService;
import com.je.common.base.util.StringUtil;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.ibatis.extension.enums.Conditions;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public abstract class AbstractQueryBuilderService implements QueryBuilderService {

    private static final Logger logger = LoggerFactory.getLogger(AbstractQueryBuilderService.class);

    @Autowired
    private SystemVariableRpcService systemVariableRpcService;

    /**
     * object2string
     */
    public static final Function<Object, String> O2S = object -> object != null ? object.toString() : null;

    /**
     * object2boolean
     */
    public static final Predicate<Object> O2B = object -> object != null && "1".equals(object.toString());

    /**
     * {var} 变量正则匹配
     */
    public static final Pattern pattern = Pattern.compile("(\\{(.*?)\\})", Pattern.MULTILINE);


    @Autowired
    public SystemVariableRpcService systemVariableService;

    @Override
    public void condition(ConditionsWrapper wrapper, Condition p) {
        condition(wrapper, p, null);
    }

    @Override
    public ConditionsWrapper buildWrapper(NativeQuery query) {
        return buildWrapper(query, null);
    }

    @Override
    public ConditionsWrapper buildWrapper(NativeQuery query, ConditionsWrapper wrapper) {
        if (query == null && wrapper == null) {
            return null;
        }

        if (wrapper == null) {
            wrapper = ConditionsWrapper.builder();
        }

        if (!Strings.isNullOrEmpty(query.getTableCode())) {
            wrapper.table(query.getTableCode());
        }

        List<Condition> conditions = query.getConditions();
        wrapper.and(conditions != null && !conditions.isEmpty(), i -> {
            conditions.forEach(p -> {
                condition(i, p);
            });
        });

        String sql = query.getSqlBuffer();
        if (!Strings.isNullOrEmpty(sql)) {
            if (query.getParams() != null && !query.getParams().isEmpty()) {
                wrapper.apply(sql, query.getParams().toArray());
            } else {
                wrapper.apply(sql);
            }
        }

        //group by
        List<GroupBy> groupBys = query.getGroupBys();
        if (groupBys != null && !groupBys.isEmpty()) {
            String[] groupedColumns = groupBys.stream().map(GroupBy::getColumn).collect(Collectors.joining(",")).split(",");
            wrapper.groupBy(groupedColumns);
        }

        //order
        List<Order> orders = query.getOrders();
        if (orders != null && !orders.isEmpty()) {
            for (Order each : orders) {
                if ("".equals(each.getType()) || "asc".equalsIgnoreCase(each.getType())) {
                    wrapper.orderByAsc(each.getCode());
                } else {
                    wrapper.orderByDesc(each.getCode());
                }
            }
        }
        return wrapper;
    }

    @Override
    public ConditionsWrapper buildWrapper(Query query) {
        return buildWrapper(query, ConditionsWrapper.builder());
    }

    /**
     * condition
     *
     * @param wrapper   条件构造器
     * @param p         条件对象
     * @param variables 变量
     * @return void
     */
    @Override
    public void condition(ConditionsWrapper wrapper, Condition p, Map<String, Object> variables) {
        //校验是否传入条件
        if (p == null || StringUtils.isBlank(p.getType())) {
            return;
        }
        //是否是OR 默认AND
        wrapper.or("or".equalsIgnoreCase(p.getCn()) && !wrapper.isEmpty());
        //字段名，去除空格防Sql注入
        String code = trimBlank(p.getCode());
//        if (code != null && !code.trim().matches("^[a-zA-Z\\$_][a-zA-Z\\d_]*$")) {
//            throw new PlatformException("条件不合法，请传入正规列名", PlatformExceptionEnum.UNKOWN_ERROR);
//        }
        //条件类型枚举
        ConditionEnum type = ConditionEnum.getCondition(p.getType());
        //
        Object value = p.getValue();
        //替换变量值
        if (ConditionEnum.AND != type && ConditionEnum.OR != type
                && ConditionEnum.IN != type && ConditionEnum.NOT_IN != type
                && ConditionEnum.IN_SELECT != type && ConditionEnum.NOT_IN_SELECT != type
                && ConditionEnum.BETWEEN != type
                && variables != null && !variables.isEmpty()) {
            String conditionStr = O2S.apply(p.getValue());
            value = formatSqlParameter(variables, conditionStr);
        }
        //字段条件
        if (ConditionEnum.BETWEEN == type) {
            //值转换为集合
            List list = parseList(p.getValue());
            if (list == null || list.size() < 2) {
                logger.error(" BETWEEN condition value error. -{}", p.getValue());
                throw new PlatformException(String.format(" BETWEEN condition value error. -%s", p.getValue()), PlatformExceptionEnum.JE_CORE_ERROR);
            }
            wrapper.between(code, list.get(0), list.get(1));
        } else if (ConditionEnum.EQ == type) {
            wrapper.eq(code, value);
        } else if (ConditionEnum.GE == type) {
            wrapper.ge(code, value);
        } else if (ConditionEnum.GT == type) {
            wrapper.gt(code, value);
        } else if (ConditionEnum.IN == type || ConditionEnum.NOT_IN == type) {
            //值转换为集合
            List list = parseList(p.getValue());
            //不管是否为空 都应执行
            if (ConditionEnum.IN == type) {
                wrapper.in(code, list);
            } else {
                wrapper.notIn(code, list);
            }
        } else if (ConditionEnum.IN_SELECT == type || ConditionEnum.NOT_IN_SELECT == type) {
            JSONObject inSelect = JSONObject.parseObject(O2S.apply(p.getValue()));
            //子查询表
            String table = trimBlank(inSelect.getString("table"));
            String fieldCode = trimBlank(inSelect.getString("code"));
            //子查询条件
            List<Condition> conditions = JSON.parseArray(inSelect.getString("conditions"), Condition.class);
            //构建子查询where条件
            ConditionsWrapper select = wrapper.child();
            if (conditions != null && !conditions.isEmpty()) {
                conditions.forEach(c -> condition(select, c, variables));
            }
            //添加inSelect条件
            String inSelectSql = "";
            if (Strings.isNullOrEmpty(select.getSql())) {
                inSelectSql = "SELECT " + trimBlank(fieldCode) + " FROM " + trimBlank(table);
            } else {
                inSelectSql = "SELECT " + trimBlank(fieldCode) + " FROM " + trimBlank(table) + " WHERE " + select.getSql();
            }
            if (ConditionEnum.IN_SELECT == type) {
                wrapper.inSql(code, inSelectSql);
            } else {
                wrapper.notInSql(code, inSelectSql);
            }
        } else if (ConditionEnum.NOT_NULL == type) {
            wrapper.and(i -> i.apply(code + " is not null"));
        } else if (ConditionEnum.IS_NULL == type) {
            wrapper.and(i -> i.apply(code + " is null"));
        } else if (ConditionEnum.LE == type) {
            wrapper.le(code, value);
        } else if (ConditionEnum.LIKE == type) {
            wrapper.like(code, value);
        } else if (ConditionEnum.LIKE_LEFT == type) {
            wrapper.likeLeft(code, value);
        } else if (ConditionEnum.LIKE_RIGHT == type) {
            wrapper.likeRight(code, value);
        } else if (ConditionEnum.LT == type) {
            wrapper.lt(code, value);
        } else if (ConditionEnum.NE == type) {
            wrapper.ne(code, value);
        } else if (ConditionEnum.AND == type || ConditionEnum.OR == type) {
            //获取组合条件
            List<Condition> conditions;
            if (p.getValue() instanceof JSONArray) {
                //json字符串类型
                conditions = JSON.parseArray(O2S.apply(p.getValue()), Condition.class);
            } else if (p.getValue() instanceof List) {
                //条件对象类型
                conditions = JSON.parseArray(JSON.toJSONString(p.getValue()), Condition.class);
            } else {
                //json字符串类型
                conditions = JSON.parseArray(O2S.apply(p.getValue()), Condition.class);
            }
            if (conditions.isEmpty()) {
                return;
            }
            //拼接组合条件
            Consumer<ConditionsWrapper> tConsumer = i -> {
                conditions.forEach(c -> condition(i, c, variables));
            };
            if (ConditionEnum.AND == type) {
                wrapper.and(tConsumer);
            } else {
                wrapper.or(tConsumer);
            }
        }
    }

    /**
     * Object 转 Collection
     *
     * @param value 待转换对象
     * @return 集合
     */
    public List parseList(Object value) {
        List list = null;
        if (value instanceof List) {
            list = (List) value;
        } else if (value instanceof Object[]) {
            list = Arrays.asList((Object[]) value);
        } else if (value != null) {
            String str = O2S.apply(value);
            //json字符串类型
            try {
                list = JSON.parseArray(str, Condition.class);
            } catch (Exception e) {
                //异常情况视为逗号分隔数据
                list = Arrays.asList(str.split(","));
            }
        }
        return list;
    }

    @Override
    public String formatVariable(ConditionsWrapper wrapper, Object sql) {
        return formatVariable(wrapper, sql, null);
    }

    @Override
    public String formatVariable(ConditionsWrapper wrapper, Object sql, Map<String, Object> values) {

        String sqlStr = O2S.apply(sql);
        Map<String, Object> variables = new HashMap<>(10);

        //添加默认变量
        Map<String, Object> systemVariable = systemVariableRpcService.systemVariables();
        Optional.ofNullable(systemVariable).ifPresent(variables::putAll);
        //添加变量
        Optional.ofNullable(values).ifPresent(variables::putAll);

        //值集合
        List<Object> matchValues = new ArrayList<>();

        //值匹配
        //'{var}'
        sqlStr = matchParameter(variables, matchValues, sqlStr, "('\\{(.*?)\\}')", 2, 2, 0);
        //'%{var}%'
        sqlStr = matchParameter(variables, matchValues, sqlStr, "('%\\{(.*?)\\}%')", 3, 3, 3);
        //'%{var}'
        sqlStr = matchParameter(variables, matchValues, sqlStr, "('%\\{(.*?)\\}')", 3, 2, 1);
        //'{var}%'
        sqlStr = matchParameter(variables, matchValues, sqlStr, "('\\{(.*?)\\}%')", 2, 3, 2);
        //"{var}"
        sqlStr = matchParameter(variables, matchValues, sqlStr, "(\"\\{(.*?)\\}\")", 2, 2, 0);
        //"%{var}%"
        sqlStr = matchParameter(variables, matchValues, sqlStr, "(\"%\\{(.*?)\\}%\")", 3, 3, 3);
        //"%{var}"
        sqlStr = matchParameter(variables, matchValues, sqlStr, "(\"%\\{(.*?)\\}\")", 3, 2, 1);
        //"{var}%"
        sqlStr = matchParameter(variables, matchValues, sqlStr, "(\"\\{(.*?)\\}%\")", 2, 3, 2);
        //{var}
        sqlStr = matchParameter(variables, matchValues, sqlStr, "(\\{(.*?)\\})", 1, 1, 0);

        //替换 #1#
        for (int i = 0; i < matchValues.size(); i++) {

            //值
            Object val = matchValues.get(i);
            //值集合
            List<Object> vals = null;
            if (val instanceof Collection) {
                //集合类型
                vals = new ArrayList<>((Collection<Object>) val);
            } else if (val.getClass().isArray()) {
                //数组类型
                vals = Arrays.asList((Object[]) val);
            }

            //放入 wrapper 后的key
            String key = null;
            if (vals == null) {
                key = wrapper.put(val);
            } else if (vals.isEmpty()) {
                key = wrapper.put(null);
            } else {
                //值为集合则
                StringBuilder keyBuilder = new StringBuilder();
                for (Object obj : vals) {
                    keyBuilder.append(wrapper.put(obj)).append(Conditions.COMMA);
                }
                //去掉末尾逗号
                keyBuilder.setLength(keyBuilder.length() - 1);
                key = keyBuilder.toString();
            }
            sqlStr = sqlStr.replaceAll(String.format("#@@%s@@#", i), key);
        }
        return sqlStr;
    }

    /**
     * 获取匹配的字符串
     *
     * @param variables 变量集合
     * @param values    值集合
     * @param sqlStr    sql语句
     * @param regex     正则
     * @param start     正则变量前置无效字符
     * @param end       正则变量后置无效字符
     * @param like      1:左like;2:右like;3:全like;不匹配:无like;
     * @return java.lang.String
     */
    public String matchParameter(Map<String, Object> variables, List<Object> values, String sqlStr, String regex, int start, int end, int like) {
        Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
        Matcher matcher = pattern.matcher(sqlStr);
        while (matcher.find()) {
            String p = matcher.group();
            //变量名  @USER_CODE@,@USER.userId@ 等
            String parameter = p.substring(start, p.length() - end);
            parameter = parameter.replace("@USER.", "@JE.currentUser.");
            //获取值
            Object val = variables.get(parameter);
            //生成key
            String key = String.format("#@@%s@@#", values.size());
            //添加值
            switch (like) {
                case 1:
                    values.add("%" + val);
                    break;
                case 2:
                    values.add(val + "%");
                    break;
                case 3:
                    values.add("%" + val + "%");
                    break;
                default:
                    values.add(val);
            }
            //替换变量为key
            sqlStr = sqlStr.replace(p, key);
        }
        return sqlStr;
    }

    /**
     * 去除sql开头的and或order
     *
     * @param sqlObject and 或 order by 开头的sql
     * @return java.lang.String
     */
    @Override
    public String trimSql(Object sqlObject) {
        if (sqlObject == null) {
            return null;
        }
        String sql = sqlObject.toString();
        String andStr = Conditions.AND.getSqlSegment();
        String orderStr = Conditions.ORDER_BY.getSqlSegment();

        String upperCase = sql.toUpperCase();
        if (upperCase.trim().startsWith(andStr)) {
            int and = upperCase.indexOf(andStr);
            return sql.substring(and + andStr.length(), sql.length());
        } else if (upperCase.trim().startsWith(orderStr)) {
            int order = upperCase.indexOf(orderStr);
            return sql.substring(order + orderStr.length(), sql.length());
        } else {
            return sql;
        }
    }

    /**
     * 去除字符串中所有空格
     *
     * @param str 字符串
     * @return java.lang.String
     */
    @Override
    public String trimBlank(String str) {
        if (str == null) {
            return null;
        } else {
            return str.replaceAll("\\s*", "");
        }
    }

    @Override
    public String formatSqlParameter(String sqlStr) {
        return formatSqlParameter(null, sqlStr);
    }

    /**
     * 替换变量，直接赋值 非预处理
     *
     * @param values 自定义变量集合
     * @param sqlStr 语句
     * @return java.lang.String
     */
    @Override
    public String formatSqlParameter(Map<String, Object> values, String sqlStr) {
        //添加默认变量
        Map<String, Object> variables = systemVariableRpcService.systemVariables();
        //添加变量
        Optional.ofNullable(values).ifPresent(variables::putAll);
        Matcher matcher = pattern.matcher(sqlStr);
        while (matcher.find()) {
            String p = matcher.group();
            //变量名  @USER_CODE@,@USER.userId@ 等
            String parameter = p.substring(1, p.length() - 1);
            parameter = parameter.replace("@USER.", "@JE.currentUser.");
            //获取值
            Object val = variables.get(parameter);
            if (val != null) {
                sqlStr = sqlStr.replace(p, val.toString());
            }
        }
        return sqlStr;
    }

    /**
     * 组合条件集合
     *
     * @param query 查询参数实体
     * @return java.util.List<com.je.core.mapper.query.Condition>
     */
    public List<Condition> mixCondition(Query query) {
        List<Condition> mixCondition = new ArrayList<>();
        Optional.ofNullable(query.getTree()).ifPresent(mixCondition::addAll);
        Optional.ofNullable(query.getQuick()).ifPresent(mixCondition::addAll);
        Optional.ofNullable(query.getGroup()).ifPresent(mixCondition::addAll);
        Optional.ofNullable(query.getColumn()).ifPresent(mixCondition::addAll);
        Optional.ofNullable(query.getWorkflow()).ifPresent(mixCondition::addAll);
        Optional.ofNullable(query.getCustom()).ifPresent(mixCondition::addAll);
        return mixCondition;
    }

    public FuncQuickPermission fastAuthVoToFuncQuickPermission(FastAuthVo fastAuthVo) {
        FuncQuickPermission funcQuickPermission = new FuncQuickPermission();
        Class clazz = funcQuickPermission.getClass();
        List<String> list = Arrays.asList(fastAuthVo.getSelectPermStr().split(","));
        try {
            for (String str : list) {
                Field field = clazz.getDeclaredField(str);
                field.setAccessible(true);
                field.setBoolean(funcQuickPermission, Boolean.TRUE);
            }
            funcQuickPermission.setActive("1".equals(fastAuthVo.getAuthOnOff()));
            if (fastAuthVo.getDeptControl() != null && StringUtil.isNotEmpty(fastAuthVo.getDeptControl().getIds())) {
                funcQuickPermission.setSeeDeptIdList(Arrays.asList(fastAuthVo.getDeptControl().getIds().split(",")));
            }

            if (fastAuthVo.getOrgControl() != null && StringUtil.isNotEmpty(fastAuthVo.getOrgControl().getIds())) {
                funcQuickPermission.setSeeOrgIdList(Arrays.asList(fastAuthVo.getOrgControl().getIds().split(",")));
            }

            if (fastAuthVo.getRoleControl() != null && StringUtil.isNotEmpty(fastAuthVo.getRoleControl().getIds())) {
                funcQuickPermission.setSeeRoleIdList(Arrays.asList(fastAuthVo.getRoleControl().getIds().split(",")));
            }

            if (fastAuthVo.getUserControl() != null && StringUtil.isNotEmpty(fastAuthVo.getUserControl().getIds())) {
                funcQuickPermission.setSeeUserIdList(Arrays.asList(fastAuthVo.getUserControl().getIds().split(",")));
            }
            return funcQuickPermission;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

}
